﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace blp2decode
{
    unsafe public class blp2decode
    {
        [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
        public struct blp2header
        {
            //[MarshalAs(UnmanagedType.ByValTStr, SizeConst=4)]
            public fixed byte ident[4];            // 0x00 - "BLP2" magic number
            public uint type;                      // 0x04 - Texture type: 0 = JPG, 1 = S3TC
            public fixed byte flags[4];            // 0x08 - Compression mode: 1 = raw, 2 = DXTC
            // 0x09 - Alpah bits 0, 1, or 8
            // 0x0A - Alpha type 0, 1, 7 or 8
            // 0x0B - Unknown flag
            public uint width;                     // 0x0C - Image width in pixels
            public uint height;                    // 0x10 - Image height in pixels
            public fixed uint mipmapoffsets[16];   // 0x14 - The file offsets of each mipmap, 0 for unused
            public fixed uint mipmaplengths[16];   // 0x54 - The length of each mipmap data block
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct rgba
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
            string str;
        }

        [DllImport("blp2decode.dll", EntryPoint = "?BLP2DECODE_FreeErrorStr@@YAXXZ")]
        public static extern void BLP2DECODE_GetLastErrorStr(char* outstr);
        //Gets string regarding the last error

        [DllImport("blp2decode.dll", EntryPoint = "?BLP2DECODE_GetLastError@@YAKXZ")]
        public static extern uint BLP2DECODE_GetLastError();
        //Returns the error number of the last error

        [DllImport("blp2decode.dll", EntryPoint = "?BLP2DECODE_BLP2toRGBA@@YAPAEPAUblp2header@@PAEKPAKQAUrgba@@KK@Z")]
        public static extern char* BLP2DECODE_BLP2toRGBA(
            ref blp2header b,        // first 0x94 (148) bytes
            void* cIn,      // the rest of the blp? or whole?
            uint insize,    // bytes
            uint* outsize,  // bytes
            void* p, //ref rgba[] p,       // returns pointer to palette - 1024 bytes
            uint width,     // out width
            uint height);   // out height

        //Converts BLP2 data into RGBA data and returns a pointer to the data

        //[DllImport("blp2decode.dll")]
        //public static extern char* BLP2DECODE_RGBAtoBLP2(char* cIn, ulong insize, blp2header* b2h, ulong width, ulong height, ulong* outsize);
        //Converts RGBA data into BLP2 data and returns a pointer to the data

        [DllImport("blp2decode.dll", EntryPoint = "?BLP2DECODE_FreeAll@@YAXXZ")]
        public static extern void BLP2DECODE_FreeAll();
        //Frees error string and data variables from memory

        [DllImport("blp2decode.dll")]
        public static extern void BLP2DECODE_FreeData();
        //Frees data variable from memory

        [DllImport("blp2decode.dll")]
        public static extern void BLP2DECODE_FreeErrorStr();
        //Frees error string variable from memory

        [DllImport("blp2decode.dll")]
        public static extern void BLP2DECODE_FreeBuffer();
        //Depreciated, still maintained for backwards compatability. Use BLP2DECODE_FreeData() instead.
    }

    public static unsafe class decode
    {
        public static Bitmap Blp2Bmp(void* blpIn,uint bytesIn,uint* bytesOut)
        {
            void* toReturn;
            Bitmap bmp = null;
            blp2decode.blp2header rtnBlpHead = new blp2decode.blp2header();

            rtnBlpHead = (blp2decode.blp2header)Marshal.PtrToStructure((IntPtr)blpIn, typeof(blp2decode.blp2header));
            IntPtr intPtr = new IntPtr(blpIn);
            intPtr = (IntPtr)((int)intPtr + (int)rtnBlpHead.mipmapoffsets[0]);

            toReturn = blp2decode.BLP2DECODE_BLP2toRGBA(
                ref rtnBlpHead,             // (Returns?) Header - 148 bytes? <-problem? ref=error 7, no ref=error 1
                (void*)intPtr,              // Pointer to blp - Address + mipmapoffsets[0]
                rtnBlpHead.mipmaplengths[0],// Bytes
                bytesOut,                   // Bytes
                null,                       // Void palette
                rtnBlpHead.width,           // Out width
                rtnBlpHead.height);         // Out height

            uint error = blp2decode.BLP2DECODE_GetLastError();
            
            if (error != 0)
            {
                Console.WriteLine("Error: " + error);
                //Deallocate memory
                blp2decode.BLP2DECODE_FreeAll();
                return bmp;
            }

            // Format and flip bitmap
            byte[] byteArr = new byte[(int)*bytesOut];

            Marshal.Copy((IntPtr)toReturn, byteArr, 0, (int)*bytesOut);
            bmp = new Bitmap((int)rtnBlpHead.width, (int)rtnBlpHead.height);

            BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size),
                ImageLockMode.WriteOnly, 
                PixelFormat.Format32bppArgb);

            Marshal.Copy(byteArr, 0, bData.Scan0, (int)*bytesOut);
            bmp.UnlockBits(bData);
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);

            //Deallocate memory
            blp2decode.BLP2DECODE_FreeAll();

            return bmp;
        }
    }
}